﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using Struct = System.Double;

namespace ArgusPCLib.Mathematics.StructWrappers
{
	public struct DoubleWrapper : ILinearScalar
	{
		Struct _value;

		public DoubleWrapper(Struct value)
		{
			this._value = value;
		}

		public static implicit operator DoubleWrapper(Struct value)
		{
			return new DoubleWrapper(value);
		}

		public static implicit operator Struct(DoubleWrapper value)
		{
			return value._value;
		}

		#region IOperand
		public static DoubleWrapper Parse(string text, string format, IFormatProvider formatProvider)
		{
			NumberStyles numberStyle;
			if (format != null)
			{
				format = format.ToLowerInvariant();
				if (format.StartsWith("c"))
					numberStyle = NumberStyles.Currency;
				else if (format.StartsWith("p"))
					numberStyle = NumberStyles.Number;
				else
					numberStyle = NumberStyles.Any;
			}
			else
				numberStyle = NumberStyles.Any;

			// if format == Percent
			if (numberStyle == NumberStyles.Number)
			{
				NumberFormatInfo nfi = (NumberFormatInfo)formatProvider;
				text = text.TrimEnd(' ');
				if (!text.EndsWith(nfi.PercentSymbol))
					throw new FormatException();
				return Struct.Parse(text.Substring(0, text.Length - nfi.PercentSymbol.Length), numberStyle, formatProvider) * 100;
			}
			return Struct.Parse(text, numberStyle, formatProvider);
		}

		public static bool TryParse(string text, string format, IFormatProvider formatProvider, out DoubleWrapper value)
		{
			NumberStyles numberStyle;
			if (format != null)
			{
				format = format.ToLowerInvariant();
				if (format.StartsWith("c"))
					numberStyle = NumberStyles.Currency;
				else if (format.StartsWith("p"))
					numberStyle = NumberStyles.Number;
				else
					numberStyle = NumberStyles.Any;
			}
			else
				numberStyle = NumberStyles.Any;

			Struct result;
			// if format == Percent
			if (numberStyle == NumberStyles.Number)
			{
				NumberFormatInfo nfi = (NumberFormatInfo)formatProvider;
				text = text.TrimEnd(' ');
				if (!text.EndsWith(nfi.PercentSymbol))
					throw new FormatException();
				
				if (Struct.TryParse(text.Substring(0, text.Length - nfi.PercentSymbol.Length), numberStyle, formatProvider, out result))
				{
					value = new DoubleWrapper(result * 100);
					return true;
				}
				else
				{
					value = new DoubleWrapper();
					return false;
				}
			}
			else if (Struct.TryParse(text, numberStyle, formatProvider, out result))
			{
				value = new DoubleWrapper(result);
				return true;
			}
			else
			{
				value = new DoubleWrapper();
				return false;
			}
		}

		public static int PartialParse(string text, string format, IFormatProvider formatProvider, out DoubleWrapper value)
		{
			NumberStyles numberStyle;
			if (format != null)
			{
				format = format.ToLowerInvariant();
				if (format.StartsWith("c"))
					numberStyle = NumberStyles.Currency;
				else
					numberStyle = NumberStyles.Any;
			}
			else
				numberStyle = NumberStyles.Any;

			NumberFormatInfo nfi = (NumberFormatInfo)formatProvider;
			Struct result = new Struct();
			int RetVal = -1;
			int i = Algorithms.Bisection<Int32Wrapper>(new Interval<Int32Wrapper>(0, text.Length), (length) =>
				{
					if (Struct.TryParse(text.Substring(0, length), numberStyle, nfi, out result))
					{
						RetVal = length;
						return -1;
					}
					else
						return 1;
				}, 0);

			value = new DoubleWrapper(result);
			if (RetVal < 1)
				return -1;
			else
				return RetVal;
		}

		IOperand IOperand.GetDeepCopy() { return new DoubleWrapper(this._value); }
		#endregion

		#region IFormattable
		public string ToString(string format, IFormatProvider formatProvider)
		{
			return this._value.ToString(format, formatProvider);
		}

		public override string ToString()
		{
			return this.ToString(null, null);
		}
		#endregion

		#region IScalar
		public static DoubleWrapper Zero { get { return new DoubleWrapper(0); } }
		public static DoubleWrapper One { get { return new DoubleWrapper(1); } }

		public static DoubleWrapper operator +(DoubleWrapper a, DoubleWrapper b)
		{
			return new DoubleWrapper(a._value + b._value);
		}

		public static DoubleWrapper operator -(DoubleWrapper a, DoubleWrapper b)
		{
			return new DoubleWrapper(a._value - b._value);
		}

		public static DoubleWrapper operator *(DoubleWrapper a, DoubleWrapper b)
		{
			return new DoubleWrapper(a._value * b._value);
		}

		public static DoubleWrapper operator /(DoubleWrapper a, DoubleWrapper b)
		{
			return new DoubleWrapper(a._value / b._value);
		}

		public static bool operator ==(DoubleWrapper a, DoubleWrapper b)
		{
			return a._value == b._value;
		}

		public static bool operator !=(DoubleWrapper a, DoubleWrapper b)
		{
			return a._value != b._value;
		}

		IScalar IScalar.Add(IScalar value) { return this + (DoubleWrapper)value; }
		IScalar IScalar.Multiply(IScalar value) { return this * (DoubleWrapper)value; }
		IScalar IScalar.Subtract(IScalar value) { return this - (DoubleWrapper)value; }
		IScalar IScalar.Divide(IScalar value) { return this / (DoubleWrapper)value; }
		bool IScalar.IsEqualTo(IScalar value) { return this == (DoubleWrapper)value; }
		#endregion

		#region ILinearScalar
		public static bool operator <(DoubleWrapper a, DoubleWrapper b)
		{
			return a._value < b._value;
		}

		public static bool operator >(DoubleWrapper a, DoubleWrapper b)
		{
			return a._value > b._value;
		}

		public static bool operator <=(DoubleWrapper a, DoubleWrapper b)
		{
			return a._value <= b._value;
		}

		public static bool operator >=(DoubleWrapper a, DoubleWrapper b)
		{
			return a._value >= b._value;
		}

		bool ILinearScalar.IsGreaterThan(ILinearScalar value) { return this > (DoubleWrapper)value; }
		bool ILinearScalar.IsSmallerThan(ILinearScalar value) { return this < (DoubleWrapper)value; }
		#endregion

		#region IComparable
		int IComparable.CompareTo(object obj)
		{
			DoubleWrapper b = (DoubleWrapper)obj;
			if (this._value < b._value)
				return -1;
			if (this._value > b._value)
				return 1;
			return 0;
		}
		#endregion
	}
}
